#define _GNU_SOURCE
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <dlfcn.h>
#include <malloc.h>
#include <execinfo.h>
#include <time.h>
#include <pthread.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include "mmd/hzmm.h"

extern void *__libc_malloc(size_t size);
extern void __libc_free(void *ptr);
extern void *__libc_realloc(void *ptr, size_t size);
extern void *__libc_memalign(size_t boundary, size_t size);
extern void backtrace_symbols_fd(void *const *buffer, int size, int fd);
extern void *__libc_calloc(size_t nmemb, size_t size);

static void *(*real_malloc)(size_t size) = __libc_malloc;
static void *(*real_calloc)(size_t nmemb, size_t size) = __libc_calloc;
static void *(*real_realloc)(void *ptr, size_t size) = __libc_realloc;
static void *(*real_memalign)(size_t alignment, size_t size) = __libc_memalign;
static void (*real_free)(void *ptr) = __libc_free;

static FILE *g_fp = NULL;
const char g_dir[128]             = "mmd";
const char g_record_path[64]      = "/hzmm"; // hzmm000.log hzmm001.log
const char g_caller_id_path[64]   = "/caller.log";
const char g_caller_name_path[64] = "/caller_name.log";
const char g_thread_id_path[64]   = "/thread.log";
const char g_thread_name_path[64] = "/thread_name.log";

const char g_op_type[][8] = {"mll", "cll", "rll", "mml", "fre"};

#define PID_MAX_NUM (3)
#define TID_MAX_NUM (30)
#define CALLER_MAX_NUM (1024)

static int g_malloc_flag = 0;

#define LINES_MAX_NUM_PER_FILE (16000000) // 约950MB
static int g_line_num   = 0;
static int g_file_index = 0;

pthread_mutex_t g_mutex = PTHREAD_MUTEX_INITIALIZER;

pid_t gettid()
{
    return syscall(SYS_gettid);
}


void record_mem_change(int type, void *caller, void *ptr, size_t size)
{
    int  sec;
    sec = time((time_t *)NULL);

    pthread_mutex_lock(&g_mutex);
    g_malloc_flag = 1;
    if (NULL == g_fp) {
        if (-1 != access(g_dir, F_OK)) {
            char path[128];
            sprintf(path, "%s%s%03d.log", g_dir, g_record_path, g_file_index);
            g_fp = fopen(path, "w");
            if (NULL == g_fp) {
                goto UNLOCK;
            }
        } else {
            goto UNLOCK;
        }
    }
    fprintf(g_fp, "%d|%d|%d|%s|%p|%p|%ld\n", getpid(), gettid(), sec, 
                                           g_op_type[type], caller, ptr, size);
    g_line_num += 1;
    if (g_line_num >= LINES_MAX_NUM_PER_FILE) {
        g_file_index += 1;
        g_line_num = 0;
        fclose(g_fp);
        g_fp = NULL;
    }
UNLOCK:
    g_malloc_flag = 0;
    pthread_mutex_unlock(&g_mutex);
}

/*******************************************************************************************/
void *my_malloc_hook(size_t size, void *caller)
{
    void *result;
    if (NULL == real_malloc) {
        real_malloc = dlsym(RTLD_NEXT, "malloc");
    }
    result = real_malloc(size);

    //record...
    //if (NULL != result)
        //update_callers(caller, result, size);
        record_mem_change(OP_MLL, caller, result, size);

    return result;
}

void *my_calloc_hook(size_t nmemb, size_t size, void *caller)
{
    void *result = NULL;
    if (NULL == real_calloc) {
        real_calloc = dlsym(RTLD_NEXT, "calloc");
    }
    result = real_calloc(nmemb , size);
    //record...
    if (NULL != result)
        record_mem_change(OP_CLL, caller, result, nmemb * size);
    return result;
}

void *my_realloc_hook(void *ptr, size_t size, void *caller)
{
    void *result;
    if (NULL == real_realloc) {
        real_realloc = dlsym(RTLD_NEXT, "realloc");
    }
    result = real_realloc(ptr, size);

    //record...
    record_mem_change(OP_FRE, caller, ptr, (size_t)0);
    if (NULL != result)
        record_mem_change(OP_RLL, caller, result, size);

    return result;
}


void *my_memalign_hook(size_t alignment, size_t size, void *caller)
{
    void *result;
    if (NULL == real_memalign) {
        real_memalign = dlsym(RTLD_NEXT, "memalign");
    }
    result = real_memalign(alignment, size);

    //record...
    if (NULL != result)
        record_mem_change(OP_MML, caller, result, size);

    return result;
}

void my_free_hook(void *ptr, void *caller)
{
    if (NULL == real_free) {
        real_free = dlsym(RTLD_NEXT, "free");
    }
    real_free(ptr);
    record_mem_change(OP_FRE, caller, ptr, (size_t)0);
}

/*******************************************************************************************/

void *malloc(size_t size)
{
    if (0 == g_malloc_flag) {
        void *caller   = __builtin_return_address(0);
        return my_malloc_hook(size, caller);
    } else {
        return real_malloc(size);
    }
}
void *calloc(size_t nmemb, size_t size)
{
    void *caller= __builtin_return_address(0);
    return my_calloc_hook(nmemb, size, caller);
}

void *realloc(void *ptr, size_t size)
{
    void *caller= __builtin_return_address(0);
    return my_realloc_hook(ptr, size, caller);
}

#if 1
void *memalign(size_t alignment, size_t size)
{
    void *caller= __builtin_return_address(0);
    return my_memalign_hook(alignment, size, caller);
}

void free(void *ptr)
{
    if (NULL == ptr)
        return;
    if (0 == g_malloc_flag) {
        void *caller= __builtin_return_address(0);
        my_free_hook(ptr, caller);
    } else {
        return real_free(ptr);
    }
}
#endif
//char *strdup(const char *str)
//{
//}

/*******************************************************************************************/


static int respond_caller_name()
{
    long caller_id;
    char buf_id[64];
    char caller_id_path[128];
    char caller_name_path[128];

    if (-1 == access(g_dir, F_OK)) return -1;

    sprintf(caller_id_path, "%s%s", g_dir, g_caller_id_path);
    FILE *fp_id   = fopen(caller_id_path,   "r");
    if (NULL == fp_id) return -1;

    sprintf(caller_name_path, "%s%s", g_dir, g_caller_name_path);
    FILE *fp_name = fopen(caller_name_path, "w");

    while (NULL != fgets(buf_id, 64, fp_id)) {
        if (35 == (int)buf_id[0]) { // 35 是 '#' 的assic码
            // 若caller list 读取完成，删除 caller list 文件，在caller name中写入结束符
            fclose(fp_id);
            fclose(fp_name);
            remove(caller_id_path);
            return 0; 
        }
        caller_id = strtol(buf_id, NULL, 16);
        fprintf(fp_name, "|0x%lx\n", caller_id);
        backtrace_symbols_fd(&caller_id, 1, fileno(fp_name));
    }
    fclose(fp_id);
    fclose(fp_name);
    return 0;
}

int get_thread_name(int pid, int tid, char *name)
{
    char path[48];
    char *read;
    char buffer[512];
    FILE *fp = NULL;
    sprintf(path, "/proc/%d/task/%d/status", pid, tid);
    if (0 != access(path, F_OK)) return -1;

    fp = fopen(path, "r");
    if (NULL == fp) return -1;

    read = fgets(buffer, 512 - 1, fp);
    if (NULL == read) {
        fclose(fp);
        return -1;
    }
    if (strlen(buffer) - 7 > 16) {
        fclose(fp);
        return -1;
    }
    int ok = sscanf(buffer, "Name:\t%s", name);
    if (ok < 1) {
        fclose(fp);
        return -1;
    }
    fclose(fp);
    return 0;
}


static int respond_thread_name()
{
    int pid, tid;
    char buf_id[64];
    char *str = NULL;
    char name[128];
    char thread_id_path[128];
    char thread_name_path[128];

    if (-1 == access(g_dir, F_OK)) return -1;

    sprintf(thread_id_path, "%s%s", g_dir, g_thread_id_path);
    FILE *fp_id   = fopen(thread_id_path,   "r");
    if (NULL == fp_id) return -1;

    sprintf(thread_name_path, "%s%s", g_dir, g_thread_name_path);
    FILE *fp_name = fopen(thread_name_path, "w");

    while (NULL != fgets(buf_id, 64, fp_id)) {
        if (35 == (int)buf_id[0]) { // 35 是 '#' 的assic码
            // 若caller list 读取完成，删除 caller list 文件，在caller name中写入结束符
            fclose(fp_id);
            fclose(fp_name);
            remove(thread_id_path);
            return 0;
        }
        str = strtok(buf_id, " ");
        pid = strtol(str, NULL, 10);
        str = strtok(NULL, " ");
        tid = strtol(str, NULL, 10);
        if (0 != get_thread_name(pid, tid, name)) continue;
        fprintf(fp_name, "%d_%d|%s\n", pid, tid, name);
    }
    fclose(fp_id);
    fclose(fp_name);
    return 0;
}

static void *pthread_respond_request(void)
{
    prctl(PR_SET_NAME, "anay_mem", 0,0,0);
    int ret = 0;
    while (1) {
        ret = respond_caller_name();
        if (-1 == ret) {
            printf("no caller request\n");
        } else {
            printf("respond caller request...\n");
        }
        ret = respond_thread_name();
        if (-1 == ret) {
            printf("no thread request\n");
        } else {
            printf("respond thread request...\n");
        }
        sleep(2);
    }
    return NULL;
}

void update_name()
{
    pthread_t pt;
    pthread_attr_t attr;
    int ret = 0;

    pthread_attr_init(&attr);
    pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
    //pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

    ret = pthread_create(&pt, &attr, pthread_respond_request, NULL);
    if (0 != ret) {
        printf("pthread create fail!\n");
    }
}

